﻿#include <system/preview_system.h>
#include <gfx/dx12/command.h>
#include <gfx/dx12/constant_buffer.h>
#include <gui/GuiBase.h>

namespace primal
{
	constexpr u64 PreviewFrameCount{ 4ull };
	constexpr u64 PreviewFrameCount_1{ PreviewFrameCount - 1ull };

	using PreviewCommand = gfx::DirectCommand<PreviewFrameCount>;

	struct FrameRenderBuffer
	{
		ID3D12Resource* color_buffer;
		ID3D12Resource* depth_buffer;
		gfx::descriptor_handle color_handle;
		gfx::descriptor_handle render_handle;
		gfx::descriptor_handle depth_handle;
		u32 width;
		u32 height;
	};

	struct Ttt
	{
		bool is_init;
		utl::unique_ptr<PreviewCommand> cmd;
		FrameRenderBuffer front_buffer;
		struct
		{
			FrameRenderBuffer buffer;
			u64 fence_value;
			gfx::ConstantBufferAllocator cbuf_allocator;
		}back_buffers[PreviewFrameCount];
		u32 current_width;
		u32 current_height;
		gfx::ComPtr<ID3D12Heap> buffers_heap;
		u64 frame_index;
		D3D12_CLEAR_VALUE color_value;
		D3D12_CLEAR_VALUE depth_value;
		d3dx::resource_barrier barrier;
	}globel{};

	void internal_resize();
	void present();
}

bool primal::preview_initialize()
{
	if(globel.is_init)
	{
		return globel.is_init;
	}

	//call depend
	{
		gfx::initialize();
	}

	// do initialize func
	{
		globel.color_value.Format = d3dx::default_formats.render_target;
		globel.depth_value.Format = d3dx::default_formats.depth_stencil;
		globel.depth_value.DepthStencil.Depth = 1.0f;
		globel.depth_value.DepthStencil.Stencil = 0ui8;
		globel.current_width = 64u;
		globel.current_height = 64u;

		globel.cmd = utl::make_unique<PreviewCommand>();
		for(u64 i = 0; i < PreviewFrameCount; ++i)
		{
			auto& back{ globel.back_buffers[i] };
			gfx::srv().allocator(back.buffer.color_handle);
			gfx::rtv().allocator(back.buffer.render_handle);
			gfx::dsv().allocator(back.buffer.depth_handle);
		}

		gfx::srv().allocator(globel.front_buffer.color_handle);
		gfx::rtv().allocator(globel.front_buffer.render_handle);
		gfx::dsv().allocator(globel.front_buffer.depth_handle);
	}

	globel.is_init = true;
	return globel.is_init;
}

void primal::preview_shutdown()
{
	if(globel.is_init)
	{
		globel.is_init = false;
	}
}

D3D12_GPU_DESCRIPTOR_HANDLE primal::current_preview()
{
	return globel.front_buffer.color_handle.gpu;
}

void primal::internal_resize()
{
	auto& back{ globel.back_buffers[globel.frame_index] };
	if((globel.current_height != back.buffer.height) || (globel.current_width != back.buffer.width))
	{
		back.buffer.width = globel.current_width;
		back.buffer.height = globel.current_height;
		gfx::release(back.buffer.color_buffer);
		gfx::release(back.buffer.depth_buffer);

		d3dx::resource_desc color_desc{};
		color_desc.as_texture_2d(back.buffer.width, back.buffer.height, d3dx::default_formats.render_target, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
		DXASSERT(gfx::device()->CreateCommittedResource(&d3dx::heap_properties.default_heap, D3D12_HEAP_FLAG_NONE, &color_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, &globel.color_value, IID_PPV_ARGS(&back.buffer.color_buffer)));

		d3dx::resource_desc depth_desc{};
		depth_desc.as_texture_2d(back.buffer.width, back.buffer.height, d3dx::default_formats.depth_stencil, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
		DXASSERT(gfx::device()->CreateCommittedResource(&d3dx::heap_properties.default_heap, D3D12_HEAP_FLAG_NONE, &depth_desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &globel.depth_value, IID_PPV_ARGS(&back.buffer.depth_buffer)));

		d3dx::view_desc vd{};
		gfx::device()->CreateShaderResourceView(back.buffer.color_buffer, vd.as_srv_texture_2d(), back.buffer.color_handle.cpu);
		gfx::device()->CreateRenderTargetView(back.buffer.color_buffer, nullptr, back.buffer.render_handle.cpu);
		d3dx::view_desc dvd{};
		gfx::device()->CreateDepthStencilView(back.buffer.depth_buffer, dvd.as_dsv_texture_2d(d3dx::default_formats.depth_stencil), back.buffer.depth_handle.cpu);
	}
}

void primal::present()
{
	auto temp_buffer{ globel.front_buffer };

	auto& back{ globel.back_buffers[globel.frame_index] };
	globel.front_buffer = back.buffer;

	back.buffer = temp_buffer;
}

void primal::preview_render(render_data data)
{
	auto& back{ globel.back_buffers[globel.frame_index] };
	globel.cmd->Begin();
	back.cbuf_allocator.reset();
	present();
	internal_resize();

	//auto cbuf = back.cbuf_allocator.allocator<int>();
	//auto cbuf1 = back.cbuf_allocator.allocator<char[257]>();
	//auto cbuf2 = back.cbuf_allocator.allocator<int>();
	//auto cbuf3 = back.cbuf_allocator.allocator<int>();

	auto list = globel.cmd->AcquireNextList();

	globel.barrier.add(back.buffer.color_buffer, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
	globel.barrier.apply(list);
	list->OMSetRenderTargets(1u, &back.buffer.render_handle.cpu, FALSE, &back.buffer.depth_handle.cpu);
	//list->ClearDepthStencilView(back.depth_handle.cpu, D3D12_CLEAR_FLAG_DEPTH, globel.depth_value.DepthStencil.Depth, globel.depth_value.DepthStencil.Stencil, 0u, nullptr);

	//do render data
	/*{
		list->SetDescriptorHeaps(1u, gfx::srv().GetAddressOf());
		ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), list);
	}*/

	globel.barrier.add(back.buffer.color_buffer, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
	globel.barrier.apply(list);
	back.fence_value = globel.cmd->End();
	++globel.frame_index;
	globel.frame_index &= PreviewFrameCount_1;
}

void primal::new_frame(u32 width, u32 height)
{
	globel.current_width = width;
	globel.current_height = height;
}


